/*
   Copyright 2008 Sourcesense Srl

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*/

package com.sourcesense.confluence.plugins.opensearch;

import java.io.IOException;
import java.io.PrintWriter;
import java.net.URLEncoder;
import java.util.Iterator;
import java.util.List;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.abdera.Abdera;
import org.apache.abdera.ext.opensearch.OpenSearchConstants;
import org.apache.abdera.ext.opensearch.model.IntegerElement;
import org.apache.abdera.i18n.templates.Template;
import org.apache.abdera.model.Entry;
import org.apache.abdera.model.Feed;
import org.apache.abdera.model.Link;
import org.apache.abdera.util.Constants;
import org.apache.lucene.search.Filter;
import org.apache.lucene.search.Query;

import bucket.core.actions.PaginationSupport;

import com.atlassian.confluence.mail.Mail;
import com.atlassian.confluence.pages.Attachment;
import com.atlassian.confluence.pages.BlogPost;
import com.atlassian.confluence.pages.Comment;
import com.atlassian.confluence.pages.Page;
import com.atlassian.confluence.search.actions.SearchBean;
import com.atlassian.confluence.search.actions.SearchQueryBean;
import com.atlassian.confluence.search.actions.SearchResultWithExcerpt;
import com.atlassian.confluence.servlet.ConfluenceHttpServlet;
import com.atlassian.confluence.spaces.SpaceDescription;
import com.atlassian.confluence.user.PersonalInformation;
import com.atlassian.spring.container.ContainerManager;


public class OpensearchPluginServlet extends ConfluenceHttpServlet {

    private Abdera abdera;
    
    @Override
    public void init(ServletConfig config) throws ServletException {
        super.init(config);
        
        // Work around quirk in class loading between Abdera and Confluence
        // @since abdera 0.5.0
        // ServiceUtil.setClassLoader(this.getClass().getClassLoader());
        
        abdera = new Abdera();
        if (abdera.getFactory() == null) {
            throw new ServletException("Abdera has no factory!");
        }
    }

    @Override
    protected void doGet(final HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        try {
            resp.setContentType(Constants.ATOM_MEDIA_TYPE);
            PrintWriter out = resp.getWriter();

            String queryString = req.getParameter("q");
            int startIndex = 1;
            if (req.getParameter("s") != null && !req.getParameter("s").equals("")) {
                startIndex = Integer.parseInt(req.getParameter("s"));
            }
            int pageSize = 10;
            if (req.getParameter("n") != null && !req.getParameter("n").equals("")) {
                pageSize = Integer.parseInt(req.getParameter("n"));
            }
            
            // Setup the Search Bean
            SearchBean searchBean = new SearchBean();
            ContainerManager.autowireComponent(searchBean);
            PaginationSupport pagination = new PaginationSupport();
            pagination.setPageSize(pageSize);
            pagination.setStartIndex(startIndex);
            searchBean.setPaginationSupport(pagination);
            // Build the Search Query
            SearchQueryBean searchQuery = new SearchQueryBean();
            ContainerManager.autowireComponent(searchQuery);
            // Set the query constraints
            searchQuery.setQueryString(queryString);
            // Build the query
            Query query = searchQuery.buildQuery();

            // Perform the Search
            Filter filter = null;
            List<Object> results = searchBean.search(query, filter, null);
            
            Feed feed = abdera.newFeed();
            for (Iterator<Object> it = results.iterator() ; it.hasNext() ; ) {
                Object object = it.next();
                if (object instanceof SearchResultWithExcerpt) {
                    SearchResultWithExcerpt result = (SearchResultWithExcerpt) object;
                    if (result.getResultObject() instanceof Page) {
                        Entry entry = feed.addEntry();
                        Page page = (Page) result.getResultObject();
                        entry.setId(page.getIdAsString());
                        entry.setTitle(page.getDisplayTitle());
                        entry.setContent(page.getExcerpt());
                        entry.addLink(SearchUtils.getContextPath() + page.getUrlPath());
                        entry.setUpdated(page.getLastModificationDate());
                        entry.addAuthor(page.getCreatorName());
                    } else if (result.getResultObject() instanceof BlogPost) {
                        Entry entry = feed.addEntry();
                        BlogPost post = (BlogPost) result.getResultObject();
                        entry.setId(post.getIdAsString());
                        entry.setTitle(post.getDisplayTitle());
                        entry.setContent(post.getExcerpt());
                        entry.addLink(SearchUtils.getContextPath() + post.getUrlPath());
                        entry.setUpdated(post.getLastModificationDate());
                        entry.addAuthor(post.getCreatorName());
                    } else if (result.getResultObject() instanceof Comment) {
                        Entry entry = feed.addEntry();
                        Comment comment = (Comment) result.getResultObject();
                        entry.setId(comment.getIdAsString());
                        entry.setTitle(comment.getDisplayTitle());
                        entry.setContent(comment.getExcerpt());
                        entry.addLink(SearchUtils.getContextPath() + comment.getUrlPath());
                        entry.setUpdated(comment.getLastModificationDate());
                        entry.addAuthor(comment.getCreatorName());
                    } else if (result.getResultObject() instanceof Mail) {
                        Entry entry = feed.addEntry();
                        Mail mail = (Mail) result.getResultObject();
                        entry.setId(mail.getIdAsString());
                        entry.setTitle(mail.getDisplayTitle());
                        entry.setContent(mail.getExcerpt());
                        entry.addLink(SearchUtils.getContextPath() + mail.getUrlPath());
                        entry.setUpdated(mail.getLastModificationDate());
                        entry.addAuthor(mail.getCreatorName());
                    } else if (result.getResultObject() instanceof Attachment) {
                        Entry entry = feed.addEntry();
                        Attachment attachment = (Attachment) result.getResultObject();
                        entry.setId(String.valueOf(attachment.getId()));
                        entry.setTitle(attachment.getDisplayTitle());
                        entry.addLink(SearchUtils.getContextPath() + attachment.getUrlPath());
                        entry.setUpdated(attachment.getLastModificationDate());
                        entry.addAuthor(attachment.getCreatorName());
                    } else if (result.getResultObject() instanceof PersonalInformation) {
                        Entry entry = feed.addEntry();
                        PersonalInformation pi = (PersonalInformation) result.getResultObject();
                        entry.setId(pi.getIdAsString());
                        entry.setTitle(pi.getDisplayTitle());
                        entry.setContent(pi.getExcerpt());
                        entry.addLink(SearchUtils.getContextPath() + pi.getUrlPath());
                        entry.setUpdated(pi.getLastModificationDate());
                        entry.addAuthor(pi.getCreatorName());
                    } else if (result.getResultObject() instanceof SpaceDescription) {
                        Entry entry = feed.addEntry();
                        SpaceDescription sd = (SpaceDescription) result.getResultObject();
                        entry.setId(sd.getIdAsString());
                        entry.setTitle(sd.getDisplayTitle());
                        entry.setContent(sd.getExcerpt());
                        entry.addLink(SearchUtils.getContextPath() + sd.getUrlPath());
                        entry.setUpdated(sd.getLastModificationDate());
                        entry.addAuthor(sd.getCreatorName());
                    }
                }
            }
            
            // Add Opensearch extensions
            IntegerElement ie = feed.addExtension(OpenSearchConstants.ITEMS_PER_PAGE);
            ie.setText(String.valueOf(pagination.getPageSize()));
            ie = feed.addExtension(OpenSearchConstants.START_INDEX);
            ie.setText(String.valueOf(pagination.getStartIndex()));
            ie = feed.addExtension(OpenSearchConstants.TOTAL_RESULTS);
            ie.setText(String.valueOf(results.size()));
            
            org.apache.abdera.ext.opensearch.model.Query qe = feed.addExtension(OpenSearchConstants.QUERY);
            qe.setRole(org.apache.abdera.ext.opensearch.model.Query.Role.REQUEST);
            qe.setSearchTerms(URLEncoder.encode(searchQuery.getQueryString(), "UTF-8"));
            
            // Add link to self
            if (true) {
                Template tpl = new Template(req.getRequestURL().toString() + "?q={searchTerms}&s={startIndex}&n={count}");
                SearchParameters params = new SearchParameters(queryString, startIndex, pageSize); 
                Link link = abdera.getFactory().newLink();
                link.setHref(tpl.expand(params));
                link.setRel(Link.REL_SELF);
                link.setMimeType(Constants.ATOM_MEDIA_TYPE);
                feed.addLink(link);
            }
            
            // Add link to next page
            if (pagination.getNextStartIndex() > 0) {
                Template tpl = new Template(req.getRequestURL().toString() + "?q={searchTerms}&s={startIndex}&n={count}");
                SearchParameters params = new SearchParameters(queryString, pagination.getNextStartIndex(), pageSize);
                Link link = abdera.getFactory().newLink();
                link.setHref(tpl.expand(params));
                link.setRel(Link.REL_NEXT);
                link.setMimeType(Constants.ATOM_MEDIA_TYPE);
                feed.addLink(link);
            }
            
            // Add link to previous page
            if (pagination.getPreviousStartIndex() > 0) {
                Template tpl = new Template(req.getRequestURL().toString() + "?q={searchTerms}&s={startIndex}&n={count}");
                SearchParameters params = new SearchParameters(queryString, pagination.getPreviousStartIndex(), pageSize); 
                Link link = abdera.getFactory().newLink();
                link.setHref(tpl.expand(params));
                link.setRel(Link.REL_PREVIOUS);
                link.setMimeType(Constants.ATOM_MEDIA_TYPE);
                feed.addLink(link);
            }
            
            feed.writeTo(out);
        } catch (Exception e) {
            // DEBUG
            e.printStackTrace();
            //
            throw new ServletException(e);
        }
    }

    public class SearchParameters {
        public String searchTerms;
        public int startIndex;
        public int count;
        
        public SearchParameters(String searchTerms, int startIndex, int count) {
            this.searchTerms = searchTerms;
            this.startIndex = startIndex;
            this.count = count;
        }
    }
}
